using System;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Web;
using System.Net.Mail;
using newtelligence.DasBlog.Runtime;
using newtelligence.DasBlog.Web.Core;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Collections.Specialized;


namespace newtelligence.DasBlog.Web.Services
{
	/// <summary>
	/// Summary description for TrackbackHandler.
	/// </summary>
    public partial class WebWebServices
	{
		[OperationContract, WebInvoke(Method="POST", UriTemplate="/trackback")]
        public void HandleTrackback( Stream stream )
        {
            string entryId;
            string title;
            string excerpt;
            string url;
            string blog_name;

            if ( !siteConfig.EnableTrackbackService )
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.ServiceUnavailable;
                WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
                return;
            }                                      

			// Try blocking them once, on the off chance they sent us a referrer
            string referrer = WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Referer];
			if (ReferralBlackList.IsBlockedReferrer(referrer))
			{
				if (siteConfig.EnableReferralUrlBlackList404s)
				{
                    WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
                    WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
               		return;
				}
			}

            entryId = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["guid"];

            
            TextReader reader = new StreamReader(stream);
            string postedArguments = reader.ReadToEnd();
            NameValueCollection formData = System.Web.HttpUtility.ParseQueryString(postedArguments);

            title = formData["title"];
            excerpt = formData["excerpt"];
            url = formData["url"];
            blog_name = formData["blog_name"];

            if ( url != null && url.Length > 0 )
            {
                try
                {
					// First line of defense, try blocking again with the URL they are tracking us back with
					if (ReferralBlackList.IsBlockedReferrer(url))
					{
						if (siteConfig.EnableReferralUrlBlackList404s)
						{
                            WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
                            WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
							return;
						}
					}

					Entry entry = dataService.GetEntry( entryId );
					
					if ( entry != null )
					{
						try
						{
							string requestBody = null;
							// see if this is a spammer
							HttpWebRequest webRequest = WebRequest.Create(url) as HttpWebRequest;
							webRequest.Method="GET";
							webRequest.UserAgent = Utils.GetUserAgent();

							HttpWebResponse response = webRequest.GetResponse() as HttpWebResponse;
								
							// now we want to get the page contents of the target body
							using (StreamReader requestReader = new StreamReader(response.GetResponseStream()))
							{
								requestBody = requestReader.ReadToEnd();
							}

							response.Close();

							// the source URL in the page could be URL encoded like the ClickThroughHandler does
							string urlEncodedBaseUrl = HttpUtility.UrlEncode(Utils.GetBaseUrl());

							// check to see if the source's page contains a link to us
							if (Regex.Match(requestBody, Utils.GetBaseUrl()).Success == false &&
								Regex.Match(requestBody, urlEncodedBaseUrl).Success == false)
							{	
								loggingService.AddEvent(new EventDataItem(
									EventCodes.TrackbackBlocked,
									HttpContext.Current.Request.UserHostAddress + " because it did not contain a link",
									Utils.GetPermaLinkUrl(entryId),
									url,
									entry.Title
									));

                                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
                                WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
								return;
							}
						}
						catch
						{
							// trackback url is not even alive
							loggingService.AddEvent(new EventDataItem(
								EventCodes.TrackbackBlocked,
								HttpContext.Current.Request.UserHostAddress + " because the server did not return a valid response",
								Utils.GetPermaLinkUrl(entryId),
								url,
								entry.Title
								));

                            WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
                            WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true; 
                            return;
						}

						// if we've gotten this far, the trackback is real and valid
						Tracking t = new Tracking();
						t.PermaLink = url;
                        t.Referer = WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Referer];
						t.RefererBlogName = blog_name;
						t.RefererExcerpt = excerpt;
						t.RefererTitle = title;
						t.RefererIPAddress = HttpContext.Current.Request.UserHostAddress;
						t.TargetEntryId = entryId;
						t.TargetTitle = entry.Title;
						t.TrackingType = TrackingType.Trackback;

						ISpamBlockingService spamBlockingService = siteConfig.SpamBlockingService;
						if (spamBlockingService != null)
						{
							bool isSpam = false;
							try 
							{
								isSpam = spamBlockingService.IsSpam(t);
							}
							catch(Exception ex)
							{
								loggingService.AddEvent(new EventDataItem(EventCodes.Error, String.Format("The external spam blocking service failed for trackback from {0}. Original exception: {1}", t.PermaLink, ex), Utils.GetPermaLinkUrl(entryId)));
							}
							if (isSpam)
							{
								//TODO: maybe we can add a configuration option to moderate trackbacks.
								// For now, we'll just avoid saving suspected spam
								loggingService.AddEvent(new EventDataItem(
									EventCodes.TrackbackBlocked,
									HttpContext.Current.Request.UserHostAddress + " because it was considered spam by the external blocking service.",
									Utils.GetPermaLinkUrl(entryId),
									url,
									entry.Title
									));
                                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
                                WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
								return;
							}
						}

						if ( siteConfig.SendTrackbacksByEmail &&
							siteConfig.SmtpServer != null && siteConfig.SmtpServer.Length > 0 )
						{
							MailMessage emailMessage = new MailMessage();
							if ( siteConfig.NotificationEMailAddress != null && 
								siteConfig.NotificationEMailAddress.Length > 0 )
							{
								emailMessage.To.Add(siteConfig.NotificationEMailAddress);
							}
							else
							{
								emailMessage.To.Add(siteConfig.Contact);
							}
							emailMessage.Subject = String.Format("Weblog trackback by '{0}' on '{1}'", t.PermaLink, t.TargetTitle);
							emailMessage.Body = String.Format("You were tracked back from\n{0}\r\non your weblog entry '{1}'\n({2}\r\n\r\nDelete Trackback: {3})", 
							                                  t.PermaLink, 
							                                  t.TargetTitle, 
							                                  Utils.GetPermaLinkUrl(entryId),
															  Utils.GetTrackbackDeleteUrl(entryId, t.PermaLink, t.TrackingType));


                            emailMessage.IsBodyHtml = false;
							emailMessage.BodyEncoding = System.Text.Encoding.UTF8;
							emailMessage.From = new MailAddress(siteConfig.Contact);
							SendMailInfo sendMailInfo = new SendMailInfo(emailMessage, siteConfig.SmtpServer,
                                siteConfig.EnableSmtpAuthentication, siteConfig.UseSSLForSMTP, siteConfig.SmtpUserName, siteConfig.SmtpPassword, siteConfig.SmtpPort);
							dataService.AddTracking(t, sendMailInfo );
						}
						else
						{
							dataService.AddTracking( t );
						}

						loggingService.AddEvent(
							new EventDataItem(
							EventCodes.TrackbackReceived,
							entry.Title,
							Utils.GetPermaLinkUrl(entryId),
							url));

						// return the correct Trackback response
						// http://www.movabletype.org/docs/mttrackback.html
						//FIX: context.Response.Write("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><response><error>0</error></response>");
						return;
					}
					
                }
				catch (System.Threading.ThreadAbortException ex)
				{
					// absorb
					ErrorTrace.Trace(System.Diagnostics.TraceLevel.Error,ex);
					return;
				}
                catch( Exception exc )
                {
                    // absorb
                    ErrorTrace.Trace(System.Diagnostics.TraceLevel.Error,exc);

					// return the correct Trackback response
					// http://www.movabletype.org/docs/mttrackback.html
					//FIX: context.Response.Write("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><response><error>1</error><message>" + exc.ToString() + "</message></response>");
					return;
                }
            }
            
            if ( entryId != null && entryId.Length > 0 )
            {
                WebOperationContext.Current.OutgoingResponse.Location = Utils.GetPermaLinkUrl(siteConfig,entryId);
                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Redirect;
                WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.Location = Utils.GetStartPageUrl(siteConfig);
                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
                WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
            }
        }
	}
}
